home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / C++ / DirectInput / DIConfig / configwnd.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-09-27  |  47.1 KB  |  2,044 lines

  1. //-----------------------------------------------------------------------------
  2. // File: configwnd.cpp
  3. //
  4. // Desc: CConfigWnd is derived from CFlexWnd. It implements the top-level
  5. //       UI window which all other windows are descendents of.
  6. //
  7. //       Functionalities handled by CConfigWnd are device tabs, Reset, Ok,
  8. //       and Cancel buttons.
  9. //
  10. // Copyright (C) Microsoft Corporation. All Rights Reserved.
  11. //-----------------------------------------------------------------------------
  12.  
  13. #include "common.hpp"
  14.  
  15.  
  16. LPCTSTR g_tszAppWindowName = _T("DINPUT Default Mapper UI");
  17.  
  18. const int WINDOW_WIDTH = 640;
  19. const int WINDOW_HEIGHT = 480;
  20. const int TABTEXTMARGINLEFT = 7;
  21. const int TABTEXTMARGINTOP = 3;
  22. const int TABTEXTMARGINRIGHT = 7;
  23. const int TABTEXTMARGINBOTTOM = 4;
  24. const int BUTTONTEXTMARGINLEFT = 7;
  25. const int BUTTONTEXTMARGINTOP = 3;
  26. const int BUTTONTEXTMARGINRIGHT = 7;
  27. const int BUTTONTEXTMARGINBOTTOM = 4;
  28. const int BARBUTTONMARGINLEFT = 9;
  29. const int BARBUTTONMARGINTOP = 4;
  30. const int BARBUTTONMARGINRIGHT = 9;
  31. const int BARBUTTONMARGINBOTTOM = 5;
  32. const int BARBUTTONSPACING = 4;
  33.  
  34. //#define WM_QUERYACTIONASSIGNEDANYWHERE (WM_USER + 4)
  35.  
  36.  
  37. CConfigWnd::CConfigWnd(CUIGlobals &uig) :
  38.     m_uig(uig),
  39.     m_bCreated(FALSE),
  40.     m_pPageFactory(NULL),
  41.     m_hPageFactoryInst(NULL),
  42.     m_pSurface(NULL),
  43.     m_pSurface3D(NULL),
  44.     m_CurSel(-1),
  45.     m_nCurGenre(0),
  46.     m_pbmTopGradient(NULL),
  47.     m_pbmBottomGradient(NULL),
  48.     m_pbmPointerEraser(NULL),
  49.     m_pbm3D(NULL),
  50.     m_p3DBits(NULL),
  51.     m_SurfFormat(D3DFMT_UNKNOWN),
  52.     m_uiPixelSize(4),
  53.     m_bBitmapsMapped(FALSE),
  54.     m_lpDI(NULL),
  55.     m_bScrollTabs(FALSE),
  56.     m_bScrollTabsLeft(FALSE),
  57.     m_bScrollTabsRight(FALSE),
  58.     m_nLeftTab(0),
  59.     m_dwInitFlags(0),
  60.     m_bHourGlass(FALSE),
  61.     m_bNeedRedraw(FALSE)
  62. {
  63.     m_lpDI = m_uig.GetDI();
  64.  
  65.     m_pSurface = m_uig.GetSurface();
  66.     m_pSurface3D = m_uig.GetSurface3D();
  67.  
  68.     if (m_pSurface != NULL || m_pSurface3D != NULL)
  69.     {
  70.         if (m_pSurface != NULL && m_pSurface3D != NULL)
  71.         {
  72.             etrace(_T("Both Surface and Surface3D are non-NULL, will use only Surface3D\n"));
  73.         
  74.             m_pSurface->Release();
  75.             m_pSurface = NULL;
  76.  
  77.             assert(m_pSurface3D != NULL);
  78.             assert(m_pSurface == NULL);
  79.         }
  80.  
  81.         assert(m_pSurface != NULL || m_pSurface3D != NULL);
  82.         assert(!(m_pSurface != NULL && m_pSurface3D != NULL));
  83.  
  84.         m_bRender3D = (m_pSurface3D != NULL);
  85.  
  86.         SetRenderMode();
  87.  
  88.         if (m_bRender3D)
  89.             Create3DBitmap();
  90.  
  91.         HDC hDC = GetRenderDC();
  92.         if (hDC != NULL)
  93.         {
  94.             m_pbmPointerEraser = CBitmap::Create(
  95.                 GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON),
  96.                 hDC);
  97.  
  98.             ReleaseRenderDC(hDC);
  99.         }
  100.         else
  101.             etrace(_T("Failed to get Render DC"));
  102.     }
  103. }
  104.  
  105. CConfigWnd::~CConfigWnd()
  106. {
  107.     ClearList();
  108.  
  109.     if (m_lpDI != NULL)
  110.         m_lpDI->Release();
  111.     m_lpDI = NULL;
  112.  
  113.     if (m_pSurface != NULL)
  114.         m_pSurface->Release();
  115.     m_pSurface = NULL;
  116.  
  117.     if (m_pSurface3D != NULL)
  118.         m_pSurface3D->Release();
  119.     m_pSurface3D = NULL;
  120.  
  121.     if (m_pPageFactory != NULL)
  122.         m_pPageFactory->Release();
  123.     m_pPageFactory = NULL;
  124.  
  125.     if (m_hPageFactoryInst != NULL)
  126.         FreeLibrary(m_hPageFactoryInst);
  127.     m_hPageFactoryInst = NULL;
  128.  
  129.     if (m_pbmPointerEraser != NULL)
  130.         delete m_pbmPointerEraser;
  131.     m_pbmPointerEraser = NULL;
  132.  
  133.     if (m_pbm3D != NULL)
  134.         delete m_pbm3D;
  135.     m_pbm3D = NULL;
  136.  
  137.     if (m_pbmTopGradient != NULL)
  138.         delete m_pbmTopGradient;
  139.     m_pbmTopGradient = NULL;
  140.  
  141.     if (m_pbmBottomGradient != NULL)
  142.         delete m_pbmBottomGradient;
  143.     m_pbmBottomGradient = NULL;
  144. }
  145.  
  146. HWND CMouseTrap::Create(HWND hParent, BOOL bInRenderMode)
  147. {
  148.     if (m_hWnd)
  149.         return m_hWnd;
  150.  
  151.     m_hParent = hParent;
  152.     int sx = GetSystemMetrics(SM_CXSCREEN);
  153.     int sy = GetSystemMetrics(SM_CYSCREEN);
  154.     RECT rect = {0, 0, sx, sy};
  155.  
  156.     // If we are not in render mode, the trap window is exactly the same as the parent window
  157.     if (!bInRenderMode)
  158.         GetWindowRect(hParent, &rect);
  159.  
  160.     return CFlexWnd::Create(
  161.         hParent,
  162.         NULL,
  163.         WS_EX_TOPMOST,
  164.         WS_POPUP | WS_VISIBLE,
  165.         rect);
  166. }
  167.  
  168. BOOL CConfigWnd::Create(HWND hParent)
  169. {
  170.  
  171.     HRESULT hresult = PrivGetClassObject(CLSID_CDIDeviceActionConfigPage, CLSCTX_INPROC_SERVER, NULL,  IID_IClassFactory, (LPVOID*) &m_pPageFactory, &m_hPageFactoryInst);
  172.     if (FAILED(hresult))
  173.     {
  174.         m_pPageFactory = NULL;
  175.         m_hPageFactoryInst = NULL;
  176.         etrace1(_T("Failed to create page classfactory, PrivGetClassObject() returned 0x%08x\n"), hresult);
  177.         return FALSE;
  178.     }
  179.  
  180.     int sx = GetSystemMetrics(SM_CXSCREEN);
  181.     int sy = GetSystemMetrics(SM_CYSCREEN);
  182.     int w = WINDOW_WIDTH;
  183.     int h = WINDOW_HEIGHT;
  184.     int rx = sx - w;
  185.     int ry = sy - h;
  186.     RECT rect = {rx / 2, ry / 2, 0, 0};
  187.     rect.right = rect.left + w;
  188.     rect.bottom = rect.top + h;
  189.  
  190.     HWND hConfigParent = hParent;
  191.  
  192.     if (InRenderMode())
  193.     {
  194.         hConfigParent = m_MouseTrap.Create(hParent, InRenderMode());
  195.         if (hConfigParent == NULL)
  196.             hConfigParent = hParent;
  197.     }
  198.  
  199.     HWND hRet = CFlexWnd::Create(
  200.         hConfigParent,
  201.         g_tszAppWindowName,
  202.         0,
  203.         WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN,
  204.         rect);
  205.  
  206.     if (hRet == NULL)
  207.         etrace(_T("CFlexWnd::Create() failed!\n"));
  208.  
  209.     // Set the cursor extent to this window if we are in render mode (full-screen)
  210.     if (InRenderMode())
  211.     {
  212.         RECT rc;
  213.         GetWindowRect(m_hWnd, &rc);
  214.         ClipCursor(&rc);
  215.     }
  216.  
  217.     return NULL != hRet;
  218. }
  219.  
  220. void CConfigWnd::SetForegroundWindow()
  221. {
  222.     // find the window
  223.     HWND hWnd = FindWindow(GetDefaultClassName(), g_tszAppWindowName);
  224.  
  225.     // activate it if found
  226.     if (NULL != hWnd)
  227.         ::SetForegroundWindow(hWnd);
  228. }
  229.  
  230. void CConfigWnd::OnPaint(HDC hDC)
  231. {
  232.     if (hDC == NULL)
  233.         return;
  234.  
  235.     SIZE topsize = GetRectSize(m_rectTopGradient);
  236.     SIZE bottomsize = GetRectSize(m_rectBottomGradient);
  237.     SIZE bsize = {max(topsize.cx, bottomsize.cx),
  238.         max(topsize.cy, bottomsize.cy)};
  239.     CBitmap *pbm = CBitmap::Create(bsize, hDC);
  240.     if (pbm == NULL)
  241.         return;
  242.     HDC hBDC = NULL, hODC = hDC;
  243.     if (m_bHourGlass)
  244.     {
  245.         if (!InRenderMode())
  246.         {
  247.             // If not in fullscreen mode, change cursor to hourglass
  248.             HCURSOR hCursor;
  249.             hCursor = LoadCursor(NULL, IDC_WAIT);
  250.             SetCursor(hCursor);
  251.         } else
  252.         {
  253.             // If in fullscreen mode, hide the cursor during reset process.
  254.             SetCursor(NULL);
  255.         }
  256.     }
  257.  
  258.     hBDC = pbm->BeginPaintInto(hDC);
  259.     if (hBDC == NULL)
  260.     {
  261.         delete pbm;
  262.         return;
  263.     }
  264.     hDC = hBDC;
  265.  
  266.     if (m_pbmTopGradient != NULL)
  267.         m_pbmTopGradient->Draw(hDC);
  268.  
  269.     {
  270.         CPaintHelper ph(m_uig, hDC);
  271.  
  272.         ph.SetElement(UIE_BORDER);
  273.  
  274.         ph.MoveTo(0, m_rectTopGradient.bottom - 1);
  275.         ph.LineTo(WINDOW_WIDTH, m_rectTopGradient.bottom - 1);
  276.  
  277.         int i;
  278.         for (i = 0; i < GetNumElements(); i++)
  279.         {
  280.             const ELEMENT &e = GetElement(i);
  281.             BOOL bSel = i == m_CurSel;
  282.  
  283.             ph.SetElement(bSel ? UIE_SELTAB : UIE_TAB);
  284.  
  285.             ph.Rectangle(e.rect);
  286.             RECT trect = e.textrect;
  287.             DrawText(hDC, e.tszCaption, -1, &trect, DT_NOCLIP | DT_NOPREFIX);
  288.  
  289.             if (bSel)
  290.             {
  291.                 ph.SetPen(UIP_BLACK);
  292.  
  293.                 ph.MoveTo(e.rect.left + 1, e.rect.bottom - 1);
  294.                 ph.LineTo(e.rect.right - 1, e.rect.bottom - 1);
  295.             }
  296.         }
  297.  
  298.         if (m_bScrollTabs && GetNumElements() > 0)
  299.         {
  300.             ph.SetElement(UIE_TABARROW);
  301.  
  302.             const ELEMENT &e = GetElement(0);
  303.             int h = e.rect.bottom - e.rect.top;
  304.  
  305.             for (i = 0; i < 2; i++)
  306.             {
  307.                 RECT &rect = i == 0 ? m_rectSTRight : m_rectSTLeft;
  308.                 BOOL bDraw = i ? m_bScrollTabsLeft : m_bScrollTabsRight;
  309.                 ph.Rectangle(rect);
  310.                 
  311.                 if (!bDraw)
  312.                     continue;
  313.  
  314.                 int d,l,r,m,t,b, f = !i, w;
  315.                 w = rect.right - rect.left;
  316.  
  317.                 l = f ? w / 4 : 3 * w / 8;
  318.                 r = f ? 5 * w / 8 : 3 * w / 4;
  319.                 d = r - l;
  320.                 m = w / 2;
  321.                 t = m - d;
  322.                 b = m + d;
  323.  
  324.                 l += rect.left;
  325.                 r += rect.left;
  326.  
  327.                 POINT p[4];
  328.                 p[3].x = p[0].x = f ? l : r;
  329.                 p[2].x = p[1].x = f ? r : l; 
  330.                 p[3].y = p[0].y = m;
  331.                 p[1].y = t;
  332.                 p[2].y = b;
  333.  
  334.                 Polyline(hDC, p, 4);
  335.             }
  336.         }
  337.     }
  338.  
  339.     pbm->Draw(hODC, topsize);
  340.     m_pbmBottomGradient->Draw(hDC);
  341.  
  342.     {
  343.         CPaintHelper ph(m_uig, hDC);
  344.  
  345.         ph.SetElement(UIE_BORDER);
  346.  
  347.         Rectangle(hDC, 0, -1, WINDOW_WIDTH,
  348.             GetRectSize(m_rectBottomGradient).cy);
  349.  
  350.         for (int i = 0; i < NUMBUTTONS; i++)
  351.         {
  352.             BOOL bOkOnly = !m_uig.InEditMode();
  353.             const BUTTON &b = m_Button[i];
  354.  
  355.  
  356.             if ( bOkOnly && i != BUTTON_CANCEL
  357.                )
  358.                 continue;
  359.  
  360.             if ( (i == BUTTON_OK || bOkOnly)
  361.                )
  362.                 ph.SetElement(UIE_DEFBUTTON);
  363.             else
  364.                 ph.SetElement(UIE_BUTTON);
  365.  
  366.             int ay = m_rectBottomGradient.top;
  367.             ph.Rectangle(b.rect.left, b.rect.top - ay, b.rect.right, b.rect.bottom - ay);
  368.             RECT trect = b.textrect;
  369.             OffsetRect(&trect, 0, -ay);
  370.             DrawText(hDC, b.tszCaption, -1, &trect, DT_NOCLIP | DT_NOPREFIX);
  371.         }
  372.     }
  373.  
  374.     pbm->Draw(hODC, m_rectBottomGradient.left, m_rectBottomGradient.top, bottomsize);
  375.  
  376.     pbm->EndPaintInto(hBDC);
  377.     delete pbm;
  378.  
  379.     hDC = hODC;
  380.  
  381.     {
  382.         CPaintHelper ph(m_uig, hDC);
  383.  
  384.         ph.SetElement(UIE_BORDER);
  385.  
  386.         ph.MoveTo(0, m_rectTopGradient.bottom);
  387.         ph.LineTo(0, m_rectBottomGradient.top);
  388.  
  389.         ph.MoveTo(WINDOW_WIDTH - 1, m_rectTopGradient.bottom);
  390.         ph.LineTo(WINDOW_WIDTH - 1, m_rectBottomGradient.top);
  391.     }
  392. }
  393.  
  394. void CConfigWnd::OnClick(POINT point, WPARAM fwKeys, BOOL bLeft)
  395. {
  396.     int i;
  397.  
  398.     // Un-highlight the current callout
  399.     SendMessage(CFlexWnd::s_CurrPageHwnd, WM_UNHIGHLIGHT, 0, 0);
  400.  
  401.     // check scroll tab buttons
  402.     if (m_bScrollTabs)
  403.         for (i = 0; i < 2; i++)
  404.         {
  405.             RECT &r = !i ? m_rectSTRight : m_rectSTLeft;
  406.             BOOL b = !i ? m_bScrollTabsRight : m_bScrollTabsLeft;
  407.             if (PtInRect(&r, point))
  408.             {
  409.                 if (b)
  410.                     ScrollTabs(!i ? -1 : 1);
  411.                 return;
  412.             }
  413.         }
  414.  
  415.     // check tabs
  416.     for (i = 0; i < GetNumElements(); i++)
  417.         if (PtInRect(&(GetElement(i).rect), point))
  418.         {
  419.             // Check if the tab is partially obscured.  If so we scroll the tab so it becomes completely visible.
  420.             POINT pt = {m_rectSTLeft.left, m_rectSTLeft.top};
  421.             if (m_bScrollTabsRight || m_bScrollTabsLeft)
  422.             {
  423.                 while (PtInRect(&(GetElement(i).rect), pt))
  424.                     ScrollTabs(1);
  425.             }
  426.             SelTab(i);
  427.             return;
  428.         }
  429.  
  430.     // check buttons
  431.     for (i = 0; i < NUMBUTTONS; i++)
  432.         if (PtInRect(&(m_Button[i].rect), point))
  433.         {
  434.             FireButton(i);
  435.             return;
  436.         }
  437. }
  438.  
  439. void CConfigWnd::ScrollTabs(int by)
  440. {
  441.     m_nLeftTab += by;
  442.     if (m_nLeftTab < 0)
  443.         m_nLeftTab = 0;
  444.     if (m_nLeftTab >= GetNumElements())
  445.         m_nLeftTab = GetNumElements() - 1;
  446.     CalcTabs();
  447.     Invalidate();
  448. }
  449.  
  450. void CConfigWnd::OnDestroy()
  451. {
  452.     ClipCursor(NULL);  // Set cursor extent to entire desktop.
  453.     if (m_bCreated)
  454.         PostQuitMessage(EXIT_SUCCESS);
  455. }
  456.  
  457. LRESULT CConfigWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
  458. {
  459.     if (!Init())
  460.     {
  461.         etrace(_T("CConfigWnd::Init() failed\n"));
  462.         return -1;
  463.     }
  464.     else
  465.         m_bCreated = TRUE;
  466.  
  467.     return 0;
  468. }
  469.  
  470. BOOL CALLBACK EnumDeviceCallback(const DIDEVICEINSTANCEW *lpdidi, LPDIRECTINPUTDEVICE8W pdiDev8W, DWORD dwFlags, DWORD dwDeviceRemaining, LPVOID pvRef)
  471. {
  472.     if (pvRef != NULL)
  473.         return ((CConfigWnd *)pvRef)->EnumDeviceCallback(lpdidi);
  474.     else
  475.         return DIENUM_STOP;
  476. }
  477.  
  478. BOOL CConfigWnd::EnumDeviceCallback(const DIDEVICEINSTANCEW *lpdidi)
  479. {
  480.     DIDEVICEINSTANCEW didi;
  481.     didi.dwSize = sizeof(DIDEVICEINSTANCEW);
  482.     didi.guidInstance = lpdidi->guidInstance;
  483.     didi.guidProduct = lpdidi->guidProduct;
  484.     didi.dwDevType = lpdidi->dwDevType;
  485.     CopyStr(didi.tszInstanceName, lpdidi->tszInstanceName, MAX_PATH);
  486.     CopyStr(didi.tszProductName, lpdidi->tszProductName, MAX_PATH);
  487.     didi.guidFFDriver = lpdidi->guidFFDriver;
  488.     didi.wUsagePage = lpdidi->wUsagePage;
  489.     didi.wUsage = lpdidi->wUsage;
  490.  
  491.     AddToList(&didi);
  492.  
  493.     return DIENUM_CONTINUE;
  494. }
  495.  
  496. // show any error message here if returning false
  497. BOOL CConfigWnd::Init(DWORD dwInitFlags)
  498. {
  499.     HRESULT hr = S_OK;
  500.     BOOL bReInit = !!(dwInitFlags & CFGWND_INIT_REINIT);
  501.  
  502.     m_dwInitFlags = dwInitFlags;
  503.     SetOnFunctionExit<DWORD> _set_m_dwInitFlags(m_dwInitFlags, 0);
  504.  
  505.     // make sure we have DI
  506.     assert(m_lpDI != NULL);
  507.     if (m_lpDI == NULL)
  508.     {
  509.         etrace(_T("NULL m_lpDI\n"));
  510.         return FALSE;
  511.     }
  512.  
  513.     if (!(dwInitFlags & CFGWND_INIT_RESET))
  514.     {
  515.         // If we are not doing reset, clear device list then re-enumerate and rebuild.
  516.  
  517.         // clear list
  518.         ClearList();
  519.  
  520.         // enum devices
  521.         {
  522.             DWORD dwFlags = DIEDBSFL_ATTACHEDONLY;
  523.             hr = m_lpDI->EnumDevicesBySemantics(NULL, (LPDIACTIONFORMATW)&m_uig.RefMasterAcFor(m_nCurGenre), ::EnumDeviceCallback, (LPVOID)this, dwFlags);
  524.         }
  525.     } else
  526.     {
  527.         DIDEVICEINSTANCEW didiCopy;
  528.         // Saves a copy of device instance as the current ELEMENT will be freed by AddToList().
  529.         CopyMemory(&didiCopy, &GetElement(m_CurSel).didi, sizeof(didiCopy));
  530.         // If resetting, call AddToList with bReset as TRUE to just get default mappings.
  531.         AddToList(&didiCopy, TRUE);
  532.     }
  533.  
  534.     // handle potential enum failure
  535.     if (FAILED(hr))
  536.     {
  537.         etrace1(_T("EnumDevicesBySemantics() failed, returning 0x%08x\n"), hr);
  538.         return FALSE;
  539.     }
  540.  
  541.     // if there are no elements, fail
  542.     if (GetNumElements() < 1)
  543.     {
  544.         etrace(_T("No devices\n"));
  545.         return FALSE;
  546.     }
  547.  
  548.     // calculate tabs, buttons, init gradients
  549.     CalcTabs();
  550.     if (!bReInit)
  551.     {
  552.         CalcButtons();
  553.         InitGradients();
  554.  
  555.         // set the timer
  556.         if (InRenderMode())
  557.         {
  558.             if (g_fptimeSetEvent)
  559.                 g_fptimeSetEvent(20, 20, CConfigWnd::TimerProc,
  560.                                  (DWORD_PTR)m_hWnd, TIME_ONESHOT);
  561.             Render();
  562.         }
  563.     }
  564.  
  565.     // make sure all the pages are in the right place
  566.     PlacePages();
  567.  
  568.     // show the first page if we are not resetting. Show current page if we are.
  569.     int CurSel = (dwInitFlags & CFGWND_INIT_RESET) ? m_CurSel : 0;
  570.     m_CurSel = -1;
  571.     SelTab(CurSel);
  572.  
  573.     // if we're already editting the layout, set it.
  574.     // KLUDGE, set false and toggle to set
  575.     if (m_bEditLayout)
  576.     {
  577.         m_bEditLayout = FALSE;
  578.         ToggleLayoutEditting();
  579.     }
  580.  
  581.     return TRUE;
  582. }
  583.  
  584. // This is called once for each device that will get configured.
  585. int CConfigWnd::AddToList(const DIDEVICEINSTANCEW *lpdidi, BOOL bReset)
  586. {
  587.     if (lpdidi == NULL)
  588.     {
  589.         etrace(_T("NULL lpdidi"));
  590.         assert(0);
  591.         return GetNumElements();
  592.     }
  593.  
  594.     int i;
  595.  
  596.     // add an element and get it if we are not doing reset (adding new device)
  597.     if (!bReset)
  598.     {
  599.         i = GetNumElements();
  600.         m_Element.SetSize(i + 1);
  601.     }
  602.     else
  603.     {
  604.         i = m_CurSel;
  605.         ClearElement(m_CurSel, bReset);  // If resetting, clear the current ELEMENT as we will populate it below.
  606.     }
  607.  
  608.     // If we are doing reset, then we use the existing ELEMENT that this device is already using.
  609.     ELEMENT &e = bReset ? GetElement(m_CurSel) : GetElement(i);
  610.  
  611.     // set various needed variables
  612.     e.didi = *lpdidi;
  613.     e.bCalc = FALSE;
  614.     e.pUIGlobals = &m_uig;
  615.  
  616.     // create and set the device
  617.     if (m_lpDI == NULL)
  618.     {
  619.         e.lpDID = NULL;
  620.         etrace(_T("m_lpDI NULL!  Can't create this device.\n"));
  621.     }
  622.     else
  623.     {
  624.         e.lpDID = CreateDevice(e.didi.guidInstance);
  625.         if (!e.lpDID)
  626.             etrace(_T("Failed to create device!\n"));
  627.     }
  628.  
  629.     if (!bReset)
  630.     {
  631.         // Find owner of device only if we are not doing reset.
  632.         // set starting current user index
  633.         DIPROPSTRING dips;
  634.         dips.diph.dwSize = sizeof(DIPROPSTRING);
  635.         dips.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  636.         dips.diph.dwObj = DIPH_DEVICE;
  637.         dips.diph.dwHow = 0;
  638.         CopyStr(dips.wsz, "", MAX_PATH);
  639.         if (!e.lpDID)
  640.         {
  641.             etrace(_T("no lpDID, assuming device unassigned\n"));
  642.             e.nCurUser = -1;
  643.         }
  644.         else
  645.         {
  646.             HRESULT hr = e.lpDID->GetProperty(DIPROP_USERNAME, (LPDIPROPHEADER)&dips);
  647.             e.nCurUser = -1; // unassigned unless getusernameindex below works
  648.             if (FAILED(hr))
  649.                 etrace(_T("GetProperty(DIPROP_USERNAME,...) failed\n"));
  650.             else if (hr == S_FALSE)
  651.                 trace(_T("GetProperty(DIPROP_USERNAME,...) returned S_FALSE\n"));
  652.             else if (StrLen(dips.wsz) < 1)
  653.                 trace(_T("GetProperty(DIPROP_USERNAME,...) returned empty string\n"));
  654.             else
  655.             {
  656.                 e.nCurUser = m_uig.GetUserNameIndex(dips.wsz);
  657.                 if (e.nCurUser == -1)
  658.                     etrace(_T("Device assigned to user not passed to ConfigureDevices()\nConsidering unassigned now\n"));
  659.             }
  660.         }
  661.     }
  662.  
  663.     // create and set the page object
  664.     HWND hwndChild = NULL;
  665.     e.pPage = CreatePageObject(i, e, hwndChild);
  666.     if (e.pPage == NULL)
  667.         etrace(_T("Failed to create page object!\n"));
  668.     e.hWnd = hwndChild;
  669.     if (e.hWnd == NULL)
  670.         etrace(_T("CreatePageObject() returned NULL hwnd!\n"));
  671.  
  672.     // create/test the first acfor for this device with cur genre/user
  673.     LPDIACTIONFORMATW lpAcFor = NULL;
  674.     if (e.nCurUser != -1)
  675.     {
  676.         lpAcFor = e.GetAcFor(m_nCurGenre, e.nCurUser, bReset);
  677.         if (lpAcFor != NULL)
  678.             TraceActionFormat(_T("Starting Device ActionFormat:"), *lpAcFor);
  679.         else
  680.             etrace(_T("Failed to create starting ActionFormat\n"));
  681.     }
  682.     else
  683.         trace(_T("Device unassigned\n"));
  684.  
  685.     // check if anything was unsuccessful
  686.     if ((lpAcFor == NULL && e.nCurUser != -1) || e.lpDID == NULL || e.pPage == NULL || e.hWnd == NULL)
  687.     {
  688.         // clear what was successful, set the size back (remove element),
  689.         // and indicate error
  690.         ClearElement(e);
  691.         m_Element.SetSize(i);
  692.         etrace(_T("Can't add this device - Element removed\n"));
  693.     }
  694.  
  695.     trace(_T("\n"));
  696.  
  697.     return GetNumElements();
  698. }
  699.  
  700. LPDIRECTINPUTDEVICE8W CConfigWnd::CreateDevice(GUID &guid)
  701. {
  702.     LPDIRECTINPUTDEVICE8W lpDID;
  703.  
  704.     HRESULT hr = m_lpDI->CreateDevice(guid, &lpDID, NULL);
  705.     if (FAILED(hr) || lpDID == NULL)
  706.     {
  707.         etrace2(_T("Could not create device (guid %s), CreateDevice() returned 0x%08x\n"), GUIDSTR(guid), hr);
  708.         return NULL;
  709.     }
  710.     
  711.     return lpDID;
  712. }
  713.  
  714. void CConfigWnd::ClearElement(int i, BOOL bReset)
  715. {
  716.     ELEMENT &e = GetElement(i);
  717.     ClearElement(e, bReset);
  718. }
  719.  
  720. void CConfigWnd::ClearElement(ELEMENT &e, BOOL bReset)
  721. {
  722.     if (e.pPage != NULL)
  723.         DestroyPageObject(e.pPage);
  724.     if (e.lpDID != NULL)
  725.     {
  726.         e.lpDID->Release();
  727.         e.lpDID = NULL;
  728.     }
  729.     e.pPage = NULL;
  730.     e.lpDID = NULL;
  731.     e.hWnd = NULL;
  732.     e.pUIGlobals = NULL; // not freed
  733.     if (!bReset)  // Free map only if we are not resetting (delete permanently).
  734.         e.FreeMap();
  735. }
  736.  
  737. void CConfigWnd::ClearList()
  738. {
  739.     int i;
  740.     for (i = 0; i < GetNumElements(); i++)
  741.         ClearElement(i);
  742.     m_Element.RemoveAll();
  743.     assert(!GetNumElements());
  744. }
  745.  
  746. void CConfigWnd::PlacePages()
  747. {
  748.     RECT rect;
  749.     GetPageRect(rect);
  750.  
  751.     for (int i = 0; i < GetNumElements(); i++)
  752.     {
  753.         DWORD flags = SWP_NOZORDER | SWP_NOACTIVATE;
  754.         SetWindowPos(GetElement(i).hWnd, NULL,
  755.             rect.left, rect.top,
  756.             rect.right - rect.left,
  757.             rect.bottom - rect.top, flags);
  758.     }
  759. }
  760.  
  761. SIZE CConfigWnd::GetTextSize(LPCTSTR tszText)
  762. {
  763.     RECT trect = {0, 0, 1, 1};
  764.     HDC hDC = CreateCompatibleDC(NULL);
  765.     if (hDC != NULL)
  766.     {
  767.         {
  768.             CPaintHelper ph(m_uig, hDC);
  769.             ph.SetFont(UIF_FRAME);
  770.             DrawText(hDC, tszText, -1, &trect, DT_CALCRECT | DT_NOPREFIX);
  771.         }
  772.         DeleteDC(hDC);
  773.     }
  774.     SIZE size = {trect.right - trect.left, trect.bottom - trect.top};
  775.     return size;
  776. }
  777.  
  778. void CConfigWnd::InitGradients()
  779. {
  780.     if (m_pbmTopGradient == NULL)
  781.         m_pbmTopGradient = CBitmap::CreateHorzGradient(m_rectTopGradient, m_uig.GetColor(UIC_CONTROLFILL), m_uig.GetColor(UIC_CONTROLFILL));
  782.     if (m_pbmBottomGradient == NULL)
  783.         m_pbmBottomGradient = CBitmap::CreateHorzGradient(m_rectBottomGradient, m_uig.GetColor(UIC_CONTROLFILL), m_uig.GetColor(UIC_CONTROLFILL));
  784. }
  785.  
  786. void CConfigWnd::CalcTabs()
  787. {
  788.     int i, maxh = 0, lastx = 0;
  789.     for (i = 0; i < GetNumElements(); i++)
  790.     {
  791.         ELEMENT &e = GetElement(i);
  792.         CopyStr(e.tszCaption, e.didi.tszInstanceName, MAX_PATH);
  793.         e.rect.left = i > 0 ? GetElement(i - 1).rect.right - 1 : 0;
  794.         e.rect.top = 0;
  795.         SIZE tsize = GetTextSize(e.tszCaption);
  796.         e.textrect.left = e.textrect.top = 0;
  797.         e.textrect.right = tsize.cx;
  798.         e.textrect.bottom = tsize.cy;
  799.         OffsetRect(&e.textrect, e.rect.left + TABTEXTMARGINLEFT, e.rect.top + TABTEXTMARGINTOP);
  800.         int w = tsize.cx;
  801.         int h = tsize.cy;
  802.         e.rect.right = e.rect.left + TABTEXTMARGINLEFT + w + TABTEXTMARGINRIGHT + 1;
  803.         e.rect.bottom = e.rect.top + TABTEXTMARGINTOP + h + TABTEXTMARGINBOTTOM;
  804.         h = e.rect.bottom - e.rect.top;
  805.         if (h > maxh) maxh = h;
  806.         e.bCalc = TRUE;
  807.     }
  808.  
  809.     for (i = 0; i < GetNumElements(); i++)
  810.     {
  811.         ELEMENT &e = GetElement(i);
  812.         e.rect.bottom = e.rect.top + maxh;
  813.         lastx = e.rect.right;
  814.     }
  815.  
  816.     if (lastx > WINDOW_WIDTH)
  817.     {
  818.         if (!m_bScrollTabs)
  819.             m_nLeftTab = 0;
  820.         m_bScrollTabs = TRUE;
  821.     }
  822.     else
  823.     {
  824.         m_bScrollTabs = FALSE;
  825.         m_nLeftTab = 0;
  826.     }
  827.  
  828.     int cutoff = WINDOW_WIDTH;
  829.     if (m_bScrollTabs)
  830.     {
  831.         cutoff = WINDOW_WIDTH - maxh * 2;
  832.         RECT r = {WINDOW_WIDTH - maxh, 0, WINDOW_WIDTH, maxh};
  833.         m_rectSTLeft = r;
  834.         OffsetRect(&r, -(maxh - 1), 0);
  835.         m_rectSTRight = r;
  836.     }
  837.  
  838.     if (m_bScrollTabs && m_nLeftTab > 0)
  839.     {
  840.         int left = GetElement(m_nLeftTab).rect.left, right = 0;
  841.         for (i = 0; i < GetNumElements(); i++)
  842.         {
  843.             ELEMENT &e = GetElement(i);
  844.             OffsetRect(&e.rect, -left, 0);
  845.             OffsetRect(&e.textrect, -left, 0);
  846.             if (e.rect.right > right)
  847.                 right = e.rect.right;
  848.         }
  849.         lastx = right;
  850.     }
  851.  
  852.     if (m_bScrollTabs)
  853.     {
  854.         m_bScrollTabsLeft = lastx > cutoff && m_nLeftTab < GetNumElements() - 1;
  855.         m_bScrollTabsRight = m_nLeftTab > 0;
  856.     }
  857.  
  858.     RECT t = {0/*lastx*/, 0, WINDOW_WIDTH, maxh};
  859.     m_rectTopGradient = t;
  860. }
  861.  
  862. void CConfigWnd::CalcButtons()
  863. {
  864.     SIZE max = {0, 0};
  865.     int i;
  866.     for (i = 0; i < NUMBUTTONS; i++)
  867.     {
  868.         BUTTON &b = m_Button[i];
  869.  
  870.         if (!StrLen(b.tszCaption))
  871.         {
  872.             switch (i)
  873.             {
  874.                 case BUTTON_RESET:
  875.                 LoadString(g_hModule, IDS_BUTTON_RESET, b.tszCaption, MAX_PATH);
  876.                 break;
  877.  
  878.  
  879.             case BUTTON_CANCEL:
  880.                 if (m_uig.InEditMode())
  881.                 {
  882.                     LoadString(g_hModule, IDS_BUTTON_CANCEL, b.tszCaption, MAX_PATH);
  883.                     break;
  884.                 }
  885.                 // else, intentional fallthrough
  886.  
  887.             case BUTTON_OK:
  888.                 LoadString(g_hModule, IDS_BUTTON_OK, b.tszCaption, MAX_PATH);
  889.                 break;
  890.             }
  891.         }
  892.  
  893.         b.textsize = GetTextSize(b.tszCaption);
  894.  
  895.         if (b.textsize.cx > max.cx)
  896.             max.cx = b.textsize.cx;
  897.         if (b.textsize.cy > max.cy)
  898.             max.cy = b.textsize.cy;
  899.     }
  900.  
  901.     max.cx += BUTTONTEXTMARGINLEFT + BUTTONTEXTMARGINRIGHT;
  902.     max.cy += BUTTONTEXTMARGINTOP + BUTTONTEXTMARGINBOTTOM;
  903.  
  904.     m_rectBottomGradient.bottom = WINDOW_HEIGHT;
  905.     m_rectBottomGradient.top = m_rectBottomGradient.bottom - max.cy - BARBUTTONMARGINTOP - BARBUTTONMARGINBOTTOM;
  906.     m_rectBottomGradient.left = 0;
  907.     m_rectBottomGradient.right = WINDOW_WIDTH;
  908.  
  909.     for (i = 0; i < NUMBUTTONS; i++)
  910.     {
  911.         BUTTON &b = m_Button[i];
  912.  
  913.         RECT z = {0,0,0,0};
  914.  
  915.         b.rect = z;
  916.         b.rect.right = max.cx;
  917.         b.rect.bottom = max.cy;
  918.  
  919.         int by = m_rectBottomGradient.top + BARBUTTONMARGINTOP;
  920.  
  921.         switch (i)
  922.         {
  923.             case BUTTON_RESET:
  924.                 OffsetRect(&b.rect, BARBUTTONMARGINLEFT, by);
  925.                 break;
  926.  
  927.  
  928.             case BUTTON_CANCEL:
  929.                 OffsetRect(&b.rect,
  930.                 m_rectBottomGradient.right - BARBUTTONMARGINRIGHT - max.cx, by);
  931.                 break;
  932.  
  933.             case BUTTON_OK:
  934.                 OffsetRect(&b.rect,
  935.                 m_rectBottomGradient.right - BARBUTTONMARGINRIGHT - max.cx - max.cx - BARBUTTONSPACING, by);
  936.                 break;
  937.         }
  938.  
  939.         POINT m = {(b.rect.right + b.rect.left) / 2, (b.rect.bottom + b.rect.top) / 2};
  940.         b.textrect.left = m.x - b.textsize.cx / 2;
  941.         b.textrect.top = m.y - b.textsize.cy / 2;
  942.         b.textrect.right = b.textrect.left + b.textsize.cx;
  943.         b.textrect.bottom = b.textrect.top + b.textsize.cy;
  944.     }
  945. }
  946.  
  947. void CConfigWnd::GetPageRect(RECT &rect, BOOL bTemp)
  948. {
  949.     if (bTemp)
  950.     {
  951.         rect.left = 1;
  952.         rect.right = WINDOW_WIDTH - 1;
  953.         rect.top = 40;
  954.         rect.bottom = WINDOW_HEIGHT - 40;
  955.     }
  956.     else
  957.     {
  958.         rect.left = 1;
  959.         rect.right = WINDOW_WIDTH - 1;
  960.         rect.top = m_rectTopGradient.bottom;
  961.         rect.bottom = m_rectBottomGradient.top;
  962.     }
  963. }
  964.  
  965. void CConfigWnd::ToggleLayoutEditting()
  966. {
  967.     m_bEditLayout = !m_bEditLayout;
  968.  
  969.     for (int i = 0; i < GetNumElements(); i++)
  970.     {
  971.         ELEMENT &e = GetElement(i);
  972.         if (e.pPage)
  973.             e.pPage->SetEditLayout(m_bEditLayout);
  974.     }
  975. }
  976.  
  977. void CConfigWnd::FireButton(int b)
  978. {
  979.     switch(b)
  980.     {
  981.         case BUTTON_OK:
  982.             if (!m_uig.InEditMode())
  983.                 break;  // If not in edit mode, Ok button doesn't not exist so we shouldn't do anything.
  984.                 Apply();  // If we are in Edit Layout mode, do not call Apply() to save to user setting.
  985.             // intentional fallthrough
  986.  
  987.         case BUTTON_CANCEL:
  988.             Destroy();
  989.             break;
  990.  
  991.         case BUTTON_RESET:
  992.             if (m_uig.InEditMode())  // Only reset if in edit mode.  Do nothing in view mode.
  993.                 Reset();
  994.             break;
  995.  
  996.  
  997.         default:
  998.             assert(0);
  999.             break;
  1000.     }
  1001. }
  1002.  
  1003. void CConfigWnd::SelTab(int i)
  1004. {
  1005.     if (i >= 0 && i < GetNumElements())
  1006.     {
  1007.         if (i == m_CurSel)
  1008.             return;
  1009.         ShowPage(i);
  1010.         HidePage(m_CurSel);
  1011.         m_CurSel = i;
  1012.         Invalidate();
  1013.     }
  1014. }
  1015.  
  1016. PAGETYPE *CConfigWnd::CreatePageObject(int nPage, const ELEMENT &e, HWND &refhChildWnd)
  1017. {
  1018.     if (m_pPageFactory == NULL)
  1019.         return NULL;
  1020.  
  1021.     PAGETYPE *pPage = NULL;
  1022.     HRESULT hresult = m_pPageFactory->CreateInstance(NULL, IID_IDIDeviceActionConfigPage, (LPVOID*) &pPage);
  1023.     if (FAILED(hresult) || pPage == NULL)
  1024.         return NULL;
  1025.  
  1026.     DICFGPAGECREATESTRUCT cs;
  1027.     cs.dwSize = sizeof(DICFGPAGECREATESTRUCT);
  1028.     cs.nPage = nPage;
  1029.     cs.hParentWnd = m_hWnd;
  1030.     GetPageRect(cs.rect, TRUE);
  1031.     cs.hPageWnd = NULL;
  1032.     cs.didi = e.didi;
  1033.     cs.lpDID = e.lpDID;
  1034.     cs.pUIGlobals = &m_uig;
  1035.     cs.pUIFrame = dynamic_cast<IDIConfigUIFrameWindow *>(this);
  1036.  
  1037.     hresult = pPage->Create(&cs);
  1038.     if (FAILED(hresult))
  1039.     {
  1040.         etrace1(_T("pPage->Create() failed, returning 0x%08x\n"), hresult);
  1041.         pPage->Release();
  1042.         return NULL;
  1043.     }
  1044.  
  1045.     refhChildWnd = cs.hPageWnd;
  1046.  
  1047.     return pPage;
  1048. }
  1049.  
  1050. void CConfigWnd::DestroyPageObject(PAGETYPE *&pPage)
  1051. {
  1052.     if (pPage != NULL)
  1053.         pPage->Release();
  1054.     pPage = NULL;
  1055. }
  1056.  
  1057. void CConfigWnd::ShowPage(int i)
  1058. {
  1059.     if (i == -1)
  1060.         return;
  1061.  
  1062.     if (i < 0 || i >= GetNumElements())
  1063.     {
  1064.         assert(0);
  1065.         return;
  1066.     }
  1067.  
  1068.     ELEMENT &e = GetElement(i);
  1069.  
  1070.     PAGETYPE *pPage = e.pPage;
  1071.     if (pPage == NULL)
  1072.     {
  1073.         assert(0);
  1074.         return;
  1075.     }
  1076.  
  1077.     pPage->Show(e.GetAcFor(m_nCurGenre, e.nCurUser));
  1078. }
  1079.  
  1080. void CConfigWnd::HidePage(int i)
  1081. {
  1082.     if (i == -1)
  1083.         return;
  1084.  
  1085.     if (i < 0 || i >= GetNumElements())
  1086.     {
  1087.         assert(0);
  1088.         return;
  1089.     }
  1090.  
  1091.     PAGETYPE *pPage = GetElement(i).pPage;
  1092.     if (pPage == NULL)
  1093.     {
  1094.         assert(0);
  1095.         return;
  1096.     }
  1097.  
  1098.     pPage->Hide();
  1099. }
  1100.  
  1101. void CConfigWnd::OnMouseOver(POINT point, WPARAM fwKeys)
  1102. {
  1103.     int i;
  1104.  
  1105.     CFlexWnd::s_ToolTip.SetEnable(FALSE);
  1106.  
  1107.     // check scroll tab buttons
  1108.     if (m_bScrollTabs)
  1109.         for (i = 0; i < 2; i++)
  1110.         {
  1111.             RECT &r = !i ? m_rectSTRight : m_rectSTLeft;
  1112.             BOOL b = !i ? m_bScrollTabsRight : m_bScrollTabsLeft;
  1113.             if (PtInRect(&r, point))
  1114.             {
  1115.                 if (b)
  1116.                     GetElement(m_CurSel).pPage->SetInfoText(m_uig.InEditMode() ? IDS_INFOMSG_EDIT_TABSCROLL : IDS_INFOMSG_VIEW_TABSCROLL);
  1117.                 return;
  1118.             }
  1119.         }
  1120.  
  1121.     // check tabs
  1122.     for (i = 0; i < GetNumElements(); i++)
  1123.         if (PtInRect(&(GetElement(i).rect), point))
  1124.         {
  1125.             GetElement(m_CurSel).pPage->SetInfoText(m_uig.InEditMode() ? IDS_INFOMSG_EDIT_TAB : IDS_INFOMSG_VIEW_TAB);
  1126.             return;
  1127.         }
  1128.  
  1129.     // check buttons
  1130.     for (i = 0; i < NUMBUTTONS; i++)
  1131.         if (PtInRect(&(m_Button[i].rect), point))
  1132.         {
  1133.             switch(i)
  1134.             {
  1135.                 case BUTTON_OK:
  1136.                     if (m_uig.InEditMode())
  1137.                         GetElement(m_CurSel).pPage->SetInfoText(IDS_INFOMSG_EDIT_OK);
  1138.                     break;
  1139.  
  1140.                 case BUTTON_CANCEL:
  1141.                     if (m_uig.InEditMode())
  1142.                         GetElement(m_CurSel).pPage->SetInfoText(IDS_INFOMSG_EDIT_CANCEL);
  1143.                     else
  1144.                         GetElement(m_CurSel).pPage->SetInfoText(IDS_INFOMSG_VIEW_OK);
  1145.                     break;
  1146.  
  1147.                 case BUTTON_RESET:
  1148.                     if (m_uig.InEditMode())  // Only reset if in edit mode.  Do nothing in view mode.
  1149.                         GetElement(m_CurSel).pPage->SetInfoText(IDS_INFOMSG_EDIT_RESET);
  1150.                     break;
  1151.             }
  1152.             return;
  1153.         }
  1154.  
  1155.     GetElement(m_CurSel).pPage->SetInfoText(-1);
  1156. }
  1157.  
  1158. void CALLBACK CConfigWnd::TimerProc(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
  1159. {
  1160.     if (!IsWindow((HWND)dwUser)) return;  // Verify that dwUser is a valid window handle
  1161.     CConfigWnd *pCfgWnd = (CConfigWnd *)GetFlexWnd((HWND)dwUser);  // Get flex object
  1162.  
  1163.     // We use PostMessage instead of calling Render() so we stay synchronized.
  1164.     PostMessage((HWND)dwUser, WM_DIRENDER, 0, 0);
  1165. }
  1166.  
  1167. void CConfigWnd::MapBitmaps(HDC hDC)
  1168. {
  1169.     if (m_bBitmapsMapped)
  1170.         return;
  1171.  
  1172.     if (m_pbmTopGradient)
  1173.         m_pbmTopGradient->MapToDC(hDC);
  1174.     if (m_pbmBottomGradient)
  1175.         m_pbmBottomGradient->MapToDC(hDC);
  1176.  
  1177.     m_bBitmapsMapped = TRUE;
  1178. }
  1179.  
  1180. LPDIACTIONFORMATW CConfigWnd::GetCurAcFor(ELEMENT &e)
  1181. {
  1182.     return e.GetAcFor(m_nCurGenre, e.nCurUser);
  1183. }
  1184.  
  1185. BOOL CConfigWnd::IsActionAssignedAnywhere(GUID GuidInstance, int nActionIndex)
  1186. {
  1187.     // Find out which user owns the device in question first.
  1188.     int nUser = 0;
  1189.     for (int ii = 0; ii < GetNumElements(); ii++)
  1190.     {
  1191.         ELEMENT &e = GetElement(ii);
  1192.         if (IsEqualGUID(e.didi.guidInstance, GuidInstance))
  1193.         {
  1194.             nUser = e.nCurUser;
  1195.             break;
  1196.         }
  1197.     }
  1198.  
  1199.     // Now we check the actions against this user.
  1200.     for (int i = 0; i < GetNumElements(); i++)
  1201.     {
  1202.         ELEMENT &e = GetElement(i);
  1203.         const LPDIACTIONFORMATW &lpAcFor = e.GetAcFor(m_nCurGenre, nUser);
  1204.  
  1205.         if (lpAcFor == NULL)
  1206.             continue;
  1207.  
  1208.         if (nActionIndex < 0 || nActionIndex > int(lpAcFor->dwNumActions))
  1209.             continue;
  1210.  
  1211.         // If this device is not owned by this user, don't need to check.
  1212.         if (e.nCurUser != nUser)
  1213.             continue;
  1214.  
  1215.         const DIACTIONW &a = lpAcFor->rgoAction[nActionIndex];
  1216.  
  1217.         if (!IsEqualGUID(a.guidInstance, GUID_NULL))
  1218.             return TRUE;
  1219.     }
  1220.  
  1221.     return FALSE;
  1222. }
  1223.  
  1224. LRESULT CConfigWnd::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  1225. {
  1226.     switch (msg)
  1227.     {
  1228.         case WM_ACTIVATE:
  1229.             switch(wParam)
  1230.             {
  1231.                 case WA_ACTIVE:
  1232.                 case WA_CLICKACTIVE:
  1233.                     // Set the cursor extent to this window if we are in render mode because the
  1234.                     // cursor can't be drawn by us when it's not above us.
  1235.                     if (InRenderMode())
  1236.                     {
  1237.                         RECT rc;
  1238.                         GetWindowRect(m_hWnd, &rc);
  1239.                         ClipCursor(&rc);
  1240.                     }
  1241.                     // Reacquire current device
  1242.                     if (GetNumElements() && m_CurSel >= 0)
  1243.                         GetElement(m_CurSel).pPage->Reacquire();
  1244.                     break;
  1245.                 case WA_INACTIVE:
  1246.                     // Unacquire current device
  1247.                     if (GetNumElements() && m_CurSel >= 0)
  1248.                         GetElement(m_CurSel).pPage->Unacquire();
  1249.                     break;
  1250.             }
  1251.             break;
  1252.  
  1253.         case WM_DIRENDER:
  1254.             // Render message, sent by TimerProc() earlier.
  1255.             // The timer proc has request a render operation.
  1256.             Render(m_bNeedRedraw);
  1257.  
  1258.             // Set the next timer event.
  1259.             if (g_fptimeSetEvent)
  1260.                 g_fptimeSetEvent(20, 20, CConfigWnd::TimerProc,
  1261.                                  (DWORD_PTR)m_hWnd, TIME_ONESHOT);
  1262.             return 0;
  1263.  
  1264.         case WM_SETFOCUS:
  1265.             // Set the keyboard focus to the current page window.
  1266.             ShowPage(m_CurSel);  // Call Show() on current page so it can get keyboard focus.
  1267.             return 0;
  1268.  
  1269.         // WM_NCHITTEST handler is added to support moving window when in GDI mode.
  1270.         case WM_NCHITTEST:
  1271.         {
  1272.             if (InRenderMode()) break;
  1273.  
  1274.             BOOL bHitCaption = TRUE;
  1275.             POINT point = {(short)LOWORD(lParam), (short)HIWORD(lParam)};
  1276.             int i;
  1277.  
  1278.             ScreenToClient(m_hWnd, &point);
  1279.             // check scroll tab buttons
  1280.             if (m_bScrollTabs)
  1281.                 for (i = 0; i < 2; i++)
  1282.                 {
  1283.                     RECT &r = !i ? m_rectSTRight : m_rectSTLeft;
  1284.                     BOOL b = !i ? m_bScrollTabsRight : m_bScrollTabsLeft;
  1285.                     if (PtInRect(&r, point))
  1286.                     {
  1287.                         if (b)
  1288.                             bHitCaption = FALSE;
  1289.                         break;
  1290.                     }
  1291.                 }
  1292.  
  1293.             // check tabs
  1294.             for (i = 0; i < GetNumElements(); i++)
  1295.                 if (PtInRect(&(GetElement(i).rect), point))
  1296.                 {
  1297.                     bHitCaption = FALSE;
  1298.                     break;
  1299.                 }
  1300.  
  1301.             // check buttons
  1302.             for (i = 0; i < NUMBUTTONS; i++)
  1303.                 if (PtInRect(&(m_Button[i].rect), point))
  1304.                 {
  1305.                     if ((i == BUTTON_RESET || i == BUTTON_OK) && !m_uig.InEditMode()) continue;
  1306.                     bHitCaption = FALSE;
  1307.                     break;
  1308.                 }
  1309.  
  1310.             // Check Y coordinate to see if it is within the caption bar.
  1311.             if ((point.y < GetElement(0).rect.top || point.y > GetElement(0).rect.bottom) &&
  1312.                     (point.y < m_rectBottomGradient.top || point.y > m_rectBottomGradient.bottom))
  1313.                 bHitCaption = FALSE;
  1314.  
  1315.             if (bHitCaption)
  1316.             {
  1317.                 // If we are returning HTCAPTION, clear the page's info box.
  1318.                 GetElement(m_CurSel).pPage->SetInfoText(-1);
  1319.                 return HTCAPTION;
  1320.             }
  1321.             break;
  1322.         }
  1323.  
  1324.         case WM_CFGUIRESET:
  1325.         {
  1326.             CFlexWnd::s_ToolTip.SetEnable(FALSE);
  1327.             m_bHourGlass = TRUE;  // Set the flag so Render() will draw hourglass instead of arrow
  1328.             Invalidate();
  1329.             SendMessage(this->m_hWnd, WM_PAINT, 0, 0);
  1330.             if (InRenderMode())  // If in render mode, need to specifically call OnRender as sending WM_PAINT merely changes flag.
  1331.                 Render(TRUE);
  1332.             if (!Init(CFGWND_INIT_REINIT | CFGWND_INIT_RESET))
  1333.             {
  1334.                 m_uig.SetFinalResult(E_FAIL);
  1335.                 Destroy();
  1336.             }
  1337.             m_bHourGlass = FALSE;  // Change cursor back to arrow
  1338.             m_MsgBox.Destroy();
  1339.             Invalidate();
  1340.             return TRUE;
  1341.         }
  1342.  
  1343.         case WM_SETCURSOR:
  1344.             {
  1345.                 static HCURSOR hCursor = LoadCursor(NULL, IDC_ARROW);
  1346.                 ::SetCursor(InRenderMode() ? NULL : hCursor);
  1347.             }
  1348.             return TRUE;
  1349.  
  1350. //        case WM_QUERYACTIONASSIGNEDANYWHERE:
  1351. //            return IsActionAssignedAnywhere(int(wParam), int(lParam));
  1352.     }
  1353.  
  1354.     return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
  1355. }
  1356.  
  1357. HRESULT CConfigWnd::Apply()
  1358. {
  1359.     // Devices need to be in the unaquired state when SetActionMap is called.
  1360.     Unacquire();
  1361.  
  1362.     for (int i = 0; i < GetNumElements(); i++)
  1363.         GetElement(i).Apply();
  1364.  
  1365.     Reacquire();
  1366.  
  1367.     return S_OK;
  1368. }
  1369.  
  1370. int CConfigWnd::GetNumElements()
  1371. {
  1372.     return m_Element.GetSize();
  1373. }
  1374.  
  1375. ELEMENT &CConfigWnd::GetElement(int i)
  1376. {
  1377.     if (i < 0 || i >= GetNumElements())
  1378.     {
  1379.         assert(0);
  1380.         etrace1(_T("Tried to get invalid element %d\n"), i);
  1381.         return m_InvalidElement;
  1382.     }
  1383.  
  1384.     return m_Element[i];
  1385. }
  1386.  
  1387. // This function returns a pointer to the action format of the device that has the given GUID
  1388. HRESULT CConfigWnd::GetActionFormatFromInstanceGuid(LPDIACTIONFORMATW *lplpAcFor, REFGUID Guid)
  1389. {
  1390.     if (!lplpAcFor)
  1391.         return E_INVALIDARG;
  1392.  
  1393.     for (int i = 0; i < GetNumElements(); i++)
  1394.     {
  1395.         ELEMENT &e = m_Element[i];
  1396.  
  1397.         if (e.didi.guidInstance == Guid)
  1398.         {
  1399.             *lplpAcFor = GetCurAcFor(e);
  1400.             return S_OK;
  1401.         }
  1402.     }
  1403.  
  1404.     return E_INVALIDARG;
  1405. }
  1406.  
  1407. HDC CConfigWnd::GetRenderDC()
  1408. {
  1409.     assert(InRenderMode());
  1410.  
  1411.     if (m_bRender3D)
  1412.         return m_pbm3D == NULL ? NULL : m_pbm3D->BeginPaintInto();
  1413.     else
  1414.     {
  1415.         if (m_pSurface == NULL)
  1416.             return NULL;
  1417.  
  1418.         HDC hDC = NULL;
  1419.         HRESULT hr = m_pSurface->GetDC(&hDC);
  1420.         if (FAILED(hr))
  1421.             if (hr == DDERR_SURFACELOST)
  1422.             {
  1423.                 m_pSurface->Restore();    // Restore the surface
  1424.                 hr = m_pSurface->GetDC(&hDC);  // Retry
  1425.                 if (FAILED(hr))
  1426.                     return NULL;
  1427.             }
  1428.             else
  1429.                 return NULL;
  1430.  
  1431.         return hDC;
  1432.     }
  1433. }
  1434.  
  1435. void CConfigWnd::ReleaseRenderDC(HDC &phDC)
  1436. {
  1437.     assert(InRenderMode());
  1438.  
  1439.     HDC hDC = phDC;
  1440.     phDC = NULL;
  1441.  
  1442.     if (m_bRender3D)
  1443.     {
  1444.         if (m_pbm3D == NULL)
  1445.             return;
  1446.  
  1447.         m_pbm3D->EndPaintInto(hDC);
  1448.     }
  1449.     else
  1450.     {
  1451.         if (m_pSurface == NULL)
  1452.             return;
  1453.  
  1454.         m_pSurface->ReleaseDC(hDC);
  1455.     }
  1456. }
  1457.  
  1458. struct BITMAPINFO_3MASKS
  1459. {
  1460.     BITMAPINFOHEADER bmiHeader;
  1461.     RGBQUAD          bmiColors[3];
  1462. };
  1463.  
  1464. void CConfigWnd::Create3DBitmap()
  1465. {
  1466.     HDC hDC = CreateCompatibleDC(NULL);
  1467.  
  1468.     BITMAPINFO_3MASKS bmi3mask;  // BITMAPINFO with 3 DWORDs for bmiColors
  1469.     BITMAPINFO *pbmi = (BITMAPINFO*)&bmi3mask;
  1470.  
  1471.     BITMAPINFOHEADER &h = pbmi->bmiHeader;
  1472.     h.biSize = sizeof(BITMAPINFOHEADER);
  1473.     h.biWidth = WINDOW_WIDTH;
  1474.     h.biHeight = -WINDOW_HEIGHT;
  1475.     h.biPlanes = 1;
  1476.     h.biSizeImage = 0;
  1477.     h.biXPelsPerMeter = 100;
  1478.     h.biYPelsPerMeter = 100;
  1479.     h.biClrImportant = 0;
  1480.  
  1481.     // Get the surface's pixel format
  1482.     D3DSURFACE_DESC d3dsd;
  1483.     ZeroMemory(&d3dsd, sizeof(d3dsd));
  1484.     m_pSurface3D->GetDesc(&d3dsd);
  1485.     m_SurfFormat = d3dsd.Format;
  1486.     switch(d3dsd.Format)
  1487.     {
  1488.         case D3DFMT_R5G6B5:
  1489.             h.biClrUsed = 3;
  1490.             h.biBitCount = 16;
  1491.             m_uiPixelSize = 2;
  1492.             h.biCompression = BI_BITFIELDS;
  1493.             *((LPDWORD)pbmi->bmiColors) = 0xF800;
  1494.             *((LPDWORD)pbmi->bmiColors+1) = 0x07E0;
  1495.             *((LPDWORD)pbmi->bmiColors+2) = 0x001F;
  1496.             break;
  1497.  
  1498.         case D3DFMT_X1R5G5B5:
  1499.         case D3DFMT_A1R5G5B5:
  1500.             h.biClrUsed = 3;
  1501.             h.biBitCount = 16;
  1502.             m_uiPixelSize = 2;
  1503.             h.biCompression = BI_BITFIELDS;
  1504.             *((LPDWORD)pbmi->bmiColors) = 0x7C00;
  1505.             *((LPDWORD)pbmi->bmiColors+1) = 0x03E0;
  1506.             *((LPDWORD)pbmi->bmiColors+2) = 0x001F;
  1507.             break;
  1508.  
  1509.         case D3DFMT_R8G8B8:
  1510.             h.biClrUsed = 0;
  1511.             h.biBitCount = 24;
  1512.             m_uiPixelSize = 3;
  1513.             h.biCompression = BI_RGB;
  1514.             break;
  1515.  
  1516.         case D3DFMT_A8R8G8B8:
  1517.         case D3DFMT_X8R8G8B8:
  1518.         default:
  1519.             // Use 32 bits for all other formats
  1520.             h.biClrUsed = 0;
  1521.             h.biBitCount = 32;
  1522.             m_uiPixelSize = 4;
  1523.             h.biCompression = BI_RGB;
  1524.             break;
  1525.     }
  1526.  
  1527.     HBITMAP hbm = CreateDIBSection(
  1528.         hDC,
  1529.         pbmi,
  1530.         DIB_RGB_COLORS,
  1531.         &m_p3DBits,
  1532.         NULL,
  1533.         0);
  1534.  
  1535.     DeleteDC(hDC);
  1536.     hDC = NULL;
  1537.  
  1538.     if (hbm != NULL)
  1539.         m_pbm3D = CBitmap::StealToCreate(hbm);
  1540.  
  1541.     if (hbm != NULL)
  1542.         DeleteObject((HGDIOBJ)hbm);
  1543.     hbm = NULL;
  1544. }
  1545.  
  1546. void CConfigWnd::Copy3DBitmapToSurface3D()
  1547. {
  1548.     assert(m_bRender3D);
  1549.  
  1550.     if (m_p3DBits == NULL || m_pbm3D == NULL || m_pSurface3D == NULL)
  1551.     {
  1552.         etrace(_T("One or more of the vars required for Copy3DBitmapToSurface() was NULL!\n"));
  1553.         return;
  1554.     }
  1555.  
  1556.     RECT rect = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT};
  1557.  
  1558.     HRESULT hr = D3DXLoadSurfaceFromMemory(
  1559.         m_pSurface3D,
  1560.         NULL,
  1561.         NULL,//&rect,
  1562.         m_p3DBits,
  1563.         m_SurfFormat,
  1564.         WINDOW_WIDTH * m_uiPixelSize,
  1565.         NULL,
  1566.         &rect,
  1567.         D3DX_FILTER_POINT,
  1568.         0);  // Disable Color Key
  1569. }
  1570.  
  1571. void CConfigWnd::CallRenderCallback()
  1572. {
  1573.     LPDICONFIGUREDEVICESCALLBACK pCallback = m_uig.GetCallback();
  1574.     LPVOID pvRefData = m_uig.GetRefData();
  1575.  
  1576.     if (pCallback == NULL)
  1577.         return;
  1578.  
  1579.     if (m_bRender3D)
  1580.     {
  1581.         Copy3DBitmapToSurface3D();
  1582.         pCallback(m_pSurface3D, pvRefData);
  1583.     }
  1584.     else
  1585.     {
  1586.         pCallback(m_pSurface, pvRefData);
  1587.     }
  1588. }
  1589.  
  1590. void CConfigWnd::OnRender(BOOL bInternalCall)
  1591. {
  1592.     m_bNeedRedraw = TRUE;
  1593. }
  1594.  
  1595. void CConfigWnd::Render(BOOL bInternalCall)
  1596. {
  1597.     m_bNeedRedraw = FALSE;
  1598.  
  1599.     ValidateRect(m_hWnd, NULL);
  1600.  
  1601.     if (m_hWnd == NULL)
  1602.         return;
  1603.  
  1604.     HDC hDC = GetRenderDC();
  1605.     if (hDC == NULL)
  1606.         return;
  1607.  
  1608.     if (bInternalCall)
  1609.         RenderInto(hDC);
  1610.  
  1611.     static ICONINFO IconInfo;
  1612.     static HCURSOR hOldCursor = NULL;
  1613.     static HCURSOR hCursor;
  1614.     if (m_bHourGlass)
  1615.         hCursor = LoadCursor(NULL, IDC_WAIT);
  1616.     else
  1617.         hCursor = LoadCursor(NULL, IDC_ARROW);
  1618.     if (hCursor == NULL)
  1619.         return;
  1620.  
  1621.     if (hOldCursor != hCursor)
  1622.     {
  1623.         hOldCursor = hCursor;
  1624.         GetIconInfo(hCursor, &IconInfo);
  1625.  
  1626.         if (IconInfo.hbmMask)
  1627.             DeleteObject(IconInfo.hbmMask);
  1628.  
  1629.         if (IconInfo.hbmColor)
  1630.             DeleteObject(IconInfo.hbmColor);
  1631.     }
  1632.  
  1633.     POINT pt;
  1634.     GetCursorPos(&pt);
  1635.  
  1636.     ScreenToClient(m_hWnd, &pt);
  1637.  
  1638.     pt.x -= IconInfo.xHotspot;
  1639.     pt.y -= IconInfo.yHotspot;
  1640.  
  1641.     if (m_pbmPointerEraser)
  1642.         m_pbmPointerEraser->Get(hDC, pt);
  1643.  
  1644.     // If m_bHourGlass is true, we are resetting, so we don't draw mouse cursor.
  1645.     if (hCursor && !m_bHourGlass)
  1646.         DrawIcon(hDC, pt.x, pt.y, hCursor);
  1647.  
  1648.     ReleaseRenderDC(hDC);
  1649.  
  1650.     CallRenderCallback();
  1651.  
  1652.     hDC = GetRenderDC();
  1653.     if (hDC == NULL)
  1654.         return;
  1655.  
  1656.     if (m_pbmPointerEraser)
  1657.         m_pbmPointerEraser->Draw(hDC, pt);
  1658.  
  1659.     ReleaseRenderDC(hDC);
  1660. }
  1661.  
  1662. void CConfigWnd::Unacquire()
  1663. {
  1664.     for (int i = 0; i < GetNumElements(); i++)
  1665.     {
  1666.         ELEMENT &e = m_Element[i];
  1667.         if (e.pPage != NULL)
  1668.             e.pPage->Unacquire();
  1669.     }
  1670. }
  1671.  
  1672. void CConfigWnd::Reacquire()
  1673. {
  1674.     for (int i = 0; i < GetNumElements(); i++)
  1675.     {
  1676.         ELEMENT &e = m_Element[i];
  1677.         if (e.pPage != NULL)
  1678.             e.pPage->Reacquire();
  1679.     }
  1680. }
  1681.  
  1682. HRESULT CConfigWnd::Reset()
  1683. {
  1684.     RECT rect;
  1685.     int iCenterX, iCenterY;
  1686.  
  1687.     GetClientRect(&rect);
  1688.     iCenterX = (rect.left + rect.right) >> 1;
  1689.     iCenterY = (rect.top + rect.bottom) >> 1;
  1690.     rect.left = rect.right = iCenterX;
  1691.     rect.top = rect.bottom = iCenterY;
  1692.     InflateRect(&rect, g_iResetMsgBoxWidth >> 1, g_iResetMsgBoxHeight >> 1);
  1693.  
  1694.     m_MsgBox.Create(m_hWnd, rect, FALSE);
  1695.     m_MsgBox.SetNotify(m_hWnd);
  1696.     m_MsgBox.SetFont((HFONT)m_uig.GetFont(UIE_USERNAMES));
  1697.     m_MsgBox.SetColors(m_uig.GetTextColor(UIE_USERNAMES),
  1698.                            m_uig.GetBkColor(UIE_USERNAMES),
  1699.                            m_uig.GetTextColor(UIE_USERNAMESEL),
  1700.                            m_uig.GetBkColor(UIE_USERNAMESEL),
  1701.                            m_uig.GetBrushColor(UIE_USERNAMES),
  1702.                            m_uig.GetPenColor(UIE_USERNAMES));
  1703.  
  1704.     TCHAR tszResourceString[MAX_PATH];
  1705.     LoadString(g_hModule, IDS_RESETMSG, tszResourceString, MAX_PATH);
  1706.     m_MsgBox.SetText(tszResourceString);
  1707.     ::ShowWindow(m_MsgBox.m_hWnd, SW_SHOW);
  1708.     ::SetWindowPos(m_MsgBox.m_hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW);
  1709.     m_MsgBox.Invalidate();
  1710.     return S_OK;
  1711. }
  1712.  
  1713. HRESULT CConfigWnd::QueryActionAssignedAnywhere(GUID GuidInstance, int i)
  1714. {
  1715.     return IsActionAssignedAnywhere(GuidInstance, i) ? S_OK : S_FALSE;
  1716. }
  1717.  
  1718. int CConfigWnd::GetNumGenres()
  1719. {
  1720.     return m_uig.GetNumMasterAcFors();
  1721. }
  1722.  
  1723. HRESULT CConfigWnd::SetCurUser(int nPage, int nUser)
  1724. {
  1725.     // make sure we're using a valid element index
  1726.     if (nPage < 0 || nPage >= GetNumElements())
  1727.     {
  1728.         assert(0);
  1729.         return E_FAIL;
  1730.     }
  1731.  
  1732.     // get the element
  1733.     ELEMENT &e = GetElement(nPage);
  1734.     
  1735.     // don't do anything if we're already set to this user
  1736.     if (e.nCurUser == nUser)
  1737.         return S_OK;
  1738.  
  1739.     // store new curuser
  1740.     e.nCurUser = nUser;
  1741.  
  1742.     // if this page isn't the one currently shown, do nothing
  1743.     // (it'll get the new acfor when it's shown)
  1744.     if (m_CurSel != nPage)
  1745.         return S_OK;
  1746.  
  1747.     // otherwised, cycle the page to reflect change
  1748.     if (e.pPage)
  1749.         e.pPage->Unacquire();
  1750.     HidePage(nPage);
  1751.     ShowPage(nPage);
  1752.     if (e.pPage)
  1753.         e.pPage->Reacquire();
  1754.  
  1755.     return S_OK;
  1756. }
  1757.  
  1758. HRESULT CConfigWnd::SetCurGenre(int NewGenre)
  1759. {
  1760.     // if no change, do nothing
  1761.     if (NewGenre == m_nCurGenre)
  1762.         return S_OK;
  1763.  
  1764.     // make sure genre index is in range
  1765.     if (NewGenre < 0 || NewGenre >= GetNumGenres())
  1766.         return E_INVALIDARG;
  1767.  
  1768.     // set genre
  1769.     m_nCurGenre = NewGenre;
  1770.  
  1771.     // store which page is currently up
  1772.     int iOldPage = m_CurSel;
  1773.  
  1774.     // for each page...
  1775.     BOOL bShown = FALSE;
  1776.     for (int i = 0; i < GetNumElements(); i++)
  1777.     {
  1778.         ELEMENT &e = GetElement(i);
  1779.  
  1780.         // hide the page and unacquire its device
  1781.         if (e.pPage)
  1782.         {
  1783.             e.pPage->Unacquire();
  1784.             HidePage(i);
  1785.         }
  1786.  
  1787.         // show page if it was the old cur page
  1788.         if (i == iOldPage && e.pPage && GetCurAcFor(e))
  1789.         {
  1790.             ShowPage(i);
  1791.             bShown = TRUE;
  1792.         }
  1793.  
  1794.         // reacquire device
  1795.         if (e.pPage)
  1796.             e.pPage->Reacquire();
  1797.     }
  1798.  
  1799.     // if nothing was shown, show something
  1800.     if (!bShown && GetNumElements() > 0)
  1801.     {
  1802.         m_CurSel = -1;
  1803.         SelTab(0);
  1804.     }
  1805.  
  1806.     // if we showed the one we expected to show, we succeeded
  1807.     return bShown ? S_OK : E_FAIL;
  1808. }
  1809.  
  1810. int CConfigWnd::GetCurGenre()
  1811. {
  1812.     return m_nCurGenre;
  1813. }
  1814.  
  1815. HWND CConfigWnd::GetMainHWND()
  1816. {
  1817.     return m_hWnd;
  1818. }
  1819.  
  1820. // This is called by CDIDeviceActionConfigPage::DeviceUINotify.
  1821. // We scan the ELEMENT array and when we find a match, destroy and recreate the device
  1822. // object, then return it back to CDIDeviceActionConfigPage so it can update its pointer.
  1823. LPDIRECTINPUTDEVICE8W CConfigWnd::RenewDevice(GUID &GuidInstance)
  1824. {
  1825.     for (int i = 0; i < GetNumElements(); i++)
  1826.     {
  1827.         ELEMENT &e = GetElement(i);
  1828.  
  1829.         if (e.didi.guidInstance == GuidInstance)
  1830.         {
  1831.             // Releaes the instance we have
  1832.             if (e.lpDID)
  1833.             {
  1834.                 e.lpDID->Release();
  1835.                 e.lpDID = NULL;
  1836.             }
  1837.             // Recreate the device
  1838.             e.lpDID = CreateDevice(e.didi.guidInstance);
  1839.             return e.lpDID;
  1840.         }
  1841.     }
  1842.     return NULL;
  1843. }
  1844.  
  1845. LPDIACTIONFORMATW ELEMENT::GetAcFor(int nGenre, int nUser, BOOL bHwDefault)
  1846. {
  1847.     // return null if requesting for unassigned user
  1848.     if (nUser == -1)
  1849.         return NULL;
  1850.  
  1851.     // validate params
  1852.     if (!lpDID || !pUIGlobals || nGenre < 0 ||
  1853.         nGenre >= pUIGlobals->GetNumMasterAcFors() ||
  1854.         nUser < 0 || nUser >= pUIGlobals->GetNumUserNames())
  1855.     {
  1856.         etrace(_T("ELEMENT::GetAcFor(): Invalid params\n"));
  1857.         return NULL;
  1858.     }
  1859.  
  1860.     // generate dword id for map entry
  1861.     DWORD dwMap = GENREUSER2MAP(nGenre, nUser);
  1862.  
  1863.     // try to get that acfor
  1864.     LPDIACTIONFORMATW lpAcFor = NULL;
  1865.     BOOL bFound = AcForMap.Lookup(dwMap, lpAcFor);
  1866.  
  1867.     // if we found it and its not null and we are asked for hardware default setting, return it
  1868.     if (bFound && lpAcFor && !bHwDefault)
  1869.         return lpAcFor;
  1870.  
  1871.     // otherwise...  we gotta make it
  1872.     // copy it from the masteracfor for the genre
  1873.     lpAcFor = DupActionFormat(&(pUIGlobals->RefMasterAcFor(nGenre)));
  1874.     if (!lpAcFor)
  1875.     {
  1876.         etrace(_T("DupActionFormat() failed\n"));
  1877.         return NULL;
  1878.     }
  1879.  
  1880.     // build it for the user
  1881.     DWORD dwFlags = 0;
  1882.     if (bHwDefault
  1883.        )
  1884.         dwFlags |= DIDBAM_HWDEFAULTS;
  1885.     LPCWSTR wszUserName = pUIGlobals->GetUserName(nUser);
  1886.     HRESULT hr = lpDID->BuildActionMap(lpAcFor, wszUserName, dwFlags);
  1887.     if (FAILED(hr))
  1888.     {
  1889.         etrace4(_T("BuildActionMap(0x%p, %s, 0x%08x) failed, returning 0x%08x\n"), lpAcFor, QSAFESTR(wszUserName), dwFlags, hr);
  1890.         FreeActionFormatDup(lpAcFor);
  1891.         lpAcFor = NULL;
  1892.         return NULL;
  1893.     }
  1894.     else
  1895.     {
  1896.         // Now we check if the return code is DI_WRITEPROTECT.  If so, device can't be remapped.
  1897.         if (hr == DI_WRITEPROTECT)
  1898.         {
  1899.             // The way we disable mapping is to add DIA_APPFIXED flag to all actions.
  1900.             for (DWORD i = 0; i < lpAcFor->dwNumActions; ++i)
  1901.                 lpAcFor->rgoAction[i].dwFlags |= DIA_APPFIXED;
  1902.         }
  1903.     }
  1904.  
  1905.     // Here we copy the DIA_APPFIXED flag back to our action format for all DIACTION that had this.
  1906.     const DIACTIONFORMATW &MasterAcFor = pUIGlobals->RefMasterAcFor(nGenre);
  1907.     for (DWORD i = 0; i < MasterAcFor.dwNumActions; ++i)
  1908.         if (MasterAcFor.rgoAction[i].dwFlags & DIA_APPFIXED)
  1909.             lpAcFor->rgoAction[i].dwFlags |= DIA_APPFIXED;
  1910.     // set it in the map
  1911.     assert(lpAcFor != NULL);
  1912.     AcForMap.SetAt(dwMap, lpAcFor);
  1913.  
  1914.     // return it
  1915.     return lpAcFor;
  1916. }
  1917.  
  1918. void ELEMENT::FreeMap()
  1919. {
  1920.     POSITION pos = AcForMap.GetStartPosition();
  1921.     while (pos != NULL)
  1922.     {
  1923.         DWORD dwMap = (DWORD)-1;
  1924.         LPDIACTIONFORMATW lpAcFor = NULL;
  1925.         AcForMap.GetNextAssoc(pos, dwMap, lpAcFor);
  1926.  
  1927.         if (lpAcFor)
  1928.             FreeActionFormatDup(lpAcFor);
  1929.     }
  1930.     AcForMap.RemoveAll();
  1931. }
  1932.  
  1933. void ELEMENT::Apply()
  1934. {
  1935.     if (lpDID == NULL)
  1936.     {
  1937.         etrace(_T("NULL lpDID, can't apply\n"));
  1938.         return;
  1939.     }
  1940.  
  1941.     if (pUIGlobals == NULL)
  1942.     {
  1943.         etrace(_T("NULL pUIGlobals, can't apply\n"));
  1944.         return;
  1945.     }
  1946.  
  1947.     LPDIACTIONFORMATW lpAcFor = NULL;
  1948.  
  1949.     // go through the map and add the map keys to last if the user
  1950.     // is the current user assignment, or to first if not
  1951.     CList<DWORD, DWORD &> first, last;
  1952.     POSITION pos = AcForMap.GetStartPosition();
  1953.     while (pos != NULL)
  1954.     {
  1955.         DWORD dwMap = (DWORD)-1;
  1956.         lpAcFor = NULL;
  1957.         AcForMap.GetNextAssoc(pos, dwMap, lpAcFor);
  1958.         
  1959.         if (MAP2USER(dwMap) == nCurUser)
  1960.             last.AddTail(dwMap);
  1961.         else
  1962.             first.AddTail(dwMap);
  1963.     }
  1964.  
  1965.     // concatenate lists
  1966.     first.AddTail(&last);
  1967.  
  1968.     // now go through the resulting list (so that the current
  1969.     // assignments are set last) if this device is assigned.
  1970.     if (nCurUser != -1)
  1971.     {
  1972.         pos = first.GetHeadPosition();
  1973.         while (pos != NULL)
  1974.         {
  1975.             DWORD dwMap = first.GetNext(pos);
  1976.             lpAcFor = AcForMap[dwMap];
  1977.  
  1978.             if (lpAcFor == NULL)
  1979.             {
  1980.                 etrace(_T("NULL lpAcFor, can't apply\n"));
  1981.                 continue;
  1982.             }
  1983.  
  1984.             int nGenre = MAP2GENRE(dwMap);
  1985.             int nUser = MAP2USER(dwMap);
  1986.             LPCWSTR wszUserName = pUIGlobals->GetUserName(nUser);
  1987.  
  1988.             TraceActionFormat(_T("Final Device ActionFormat:"), *lpAcFor);
  1989.  
  1990.             for (DWORD j = 0; j < lpAcFor->dwNumActions; ++j)
  1991.             {
  1992.                 if( lpAcFor->rgoAction[j].dwObjID == (DWORD)-1 || IsEqualGUID(lpAcFor->rgoAction[j].guidInstance, GUID_NULL))
  1993.                 { 
  1994.                     lpAcFor->rgoAction[j].dwHow = DIAH_UNMAPPED;
  1995.                 }
  1996.                 else if( lpAcFor->rgoAction[j].dwHow & 
  1997.                         ( DIAH_USERCONFIG | DIAH_APPREQUESTED | DIAH_HWAPP | DIAH_HWDEFAULT | DIAH_DEFAULT ) )
  1998.                 {
  1999.                     lpAcFor->rgoAction[j].dwHow = DIAH_USERCONFIG;
  2000.                 }
  2001.                 else if(IsEqualGUID(didi.guidInstance,lpAcFor->rgoAction[j].guidInstance))
  2002.                 {
  2003.                     lpAcFor->rgoAction[j].dwHow = DIAH_USERCONFIG;
  2004.                 }
  2005.             }
  2006.  
  2007.             HRESULT hr;
  2008.             hr = lpDID->SetActionMap(lpAcFor, wszUserName, DIDSAM_FORCESAVE|DIDSAM_DEFAULT);
  2009.  
  2010.             if (FAILED(hr))
  2011.                 etrace1(_T("SetActionMap() failed, returning 0x%08x\n"), hr);
  2012.         }
  2013.     }  // if (nCurUser != -1)
  2014.     else
  2015.     {
  2016.         // we're unassigned, set null
  2017.  
  2018.         // we need an acfor to unassign, so get one if don't have
  2019.         // one left over from what we just did
  2020.         if (!lpAcFor)
  2021.             lpAcFor = GetAcFor(0, 0);
  2022.  
  2023.         if (!lpAcFor)
  2024.             etrace(_T("Couldn't get an acfor for unassignment\n"));
  2025.  
  2026.         HRESULT hr;
  2027.         hr = lpDID->SetActionMap(lpAcFor, NULL, DIDSAM_NOUSER);
  2028.  
  2029.         if (FAILED(hr))
  2030.             etrace1(_T("SetActionMap() failed, returning 0x%08x\n"), hr);
  2031.  
  2032.     }
  2033. }
  2034.  
  2035. int CConfigWnd::GetNumUsers()
  2036. {
  2037.     return m_uig.GetNumUserNames();
  2038. }
  2039.  
  2040. int    CConfigWnd::GetCurUser(int nPage)
  2041. {
  2042.     return GetElement(nPage).nCurUser;
  2043. }
  2044.